package cn.starremote.component.utils.encrypt;

import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Hex;

import java.security.MessageDigest;
import java.util.Arrays;

/**
 * @Auther: Wang Jianwei
 * @Date: 2019/9/12 11:02
 * @Description:  SM3工具类(杂凑算法-hash算法)
 */
@Slf4j
public class SM3Util {
    //字符编码
    public static final String CHARSET_UTF8 = "UTF-8";

    /**
     * 16进制字符串SM3生成HASH签名值算法
     * @param hexString     16进制字符串
     * @return
     */
    public static String hexEncrypt(String hexString) {
        byte[] srcData = Hex.decode(hexString);
        byte[] encrypt = encrypt(srcData);
        String cipherStr  = Hex.toHexString(encrypt);
        return cipherStr;
    }

    /**
     * 16进制字符串SM3生成HASH签名值算法
     * @param hexKey        16进制密钥
     * @param hexString     16进制字符串
     * @return
     */
    public static String hexEncrypt(String hexKey, String hexString) {
        byte[] key = Hex.decode(hexKey);
        byte[] srcData = Hex.decode(hexString);
        byte[] encrypt = encrypt(key, srcData);
        String cipherStr  = Hex.toHexString(encrypt);
        return cipherStr;
    }

    /**
     * 普通文本SM3生成HASH签名算法
     * @param plain     待签名数据
     * @return
     */
    public static String plainEncrypt(String plain) {
        // 将返回的hash值转换成16进制字符串
        String cipherStr = null;
        try {
            //将字符串转换成byte数组
            byte[] srcData = plain.getBytes(CHARSET_UTF8);
            //调用encrypt计算hash
            byte[] encrypt = encrypt(srcData);
            //将返回的hash值转换成16进制字符串
            cipherStr = Hex.toHexString(encrypt);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return cipherStr;
    }

    /**
     * 普通文本SM3生成HASH签名算法
     * @param hexKey        密钥
     * @param plain         待签名数据
     * @return
     */
    public static String plainEncrypt(String hexKey, String plain) {
        // 将返回的hash值转换成16进制字符串
        String cipherStr = null;
        try {
            //将字符串转换成byte数组
            byte[] srcData = plain.getBytes(CHARSET_UTF8);
            //密钥
            byte[] key = Hex.decode(hexKey);
            //调用encrypt计算hash
            byte[] encrypt = encrypt(key, srcData);
            //将返回的hash值转换成16进制字符串
            cipherStr = Hex.toHexString(encrypt);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return cipherStr;
    }

    /**
     * SM3计算hashCode
     * @param srcData   待计算数据
     * @return
     */
    public static byte[] encrypt(byte[] srcData) {
        SM3Digest sm3Digest = new SM3Digest();
        sm3Digest.update(srcData, 0, srcData.length);
        byte[] encrypt = new byte[sm3Digest.getDigestSize()];
        sm3Digest.doFinal(encrypt, 0);
        return encrypt;
    }

    /**
     * 通过密钥进行加密
     * @param key       密钥byte数组
     * @param srcData   被加密的byte数组
     * @return
     */
    public static byte[] encrypt(byte[] key, byte[] srcData) {
        KeyParameter keyParameter = new KeyParameter(key);
        SM3Digest digest = new SM3Digest();
        HMac mac = new HMac(digest);
        mac.init(keyParameter);
        mac.update(srcData, 0, srcData.length);
        byte[] result = new byte[mac.getMacSize()];
        mac.doFinal(result, 0);
        return result;
    }

    /**
     * SM3计算hashCode
     * @param srcData   待计算数据
     * @return
     * @throws Exception
     */
    public static byte[] encrypt_0(byte[] srcData) throws Exception {
        MessageDigest messageDigest = MessageDigest.getInstance("SM3", "BC");
        byte[] digest = messageDigest.digest(srcData);
        return digest;
    }

    /**
     * 计算SM3 Hmac值
     *
     * @param key     key值，可以是任意长度的字节数组
     * @param srcData 原文
     * @return Mac值，对于HMac-SM3来说是32字节
     */
    public static byte[] hmac(byte[] key, byte[] srcData) {
        KeyParameter keyParam = new KeyParameter(key);
        SM3Digest digest = new SM3Digest();
        HMac mac = new HMac(digest);
        mac.init(keyParam);
        mac.update(srcData, 0, srcData.length);
        byte[] result = new byte[mac.getMacSize()];
        mac.doFinal(result, 0);
        return result;
    }

    /**
     * 计算SM3摘要值
     *
     * @param srcData 原文
     * @return 摘要值，对于SM3算法来说是32字节
     */
    public static byte[] signHash(byte[] srcData) {
        SM3Digest digest = new SM3Digest();
        digest.update(srcData, 0, srcData.length);
        byte[] hash = new byte[digest.getDigestSize()];
        digest.doFinal(hash, 0);
        return hash;
    }

    /**
     * 验证摘要
     *
     * @param srcData 原文
     * @param sm3Hash 摘要值
     * @return 返回true标识验证成功，false标识验证失败
     */
    public static boolean verifySign(byte[] srcData, byte[] sm3Hash) {
        byte[] newHash = signHash(srcData);
        if (Arrays.equals(newHash, sm3Hash)) {
            return true;
        } else {
            return false;
        }
    }
}
